
#include <stdint.h>
#include <string.h>
#include "n32g45x.h"

#define N32_FLASH_BASE  0x08000000
#define N32_FLASH_SIZE  (512 * 1024) // 1MB
#define N32_PAGE_SIZE   2048        // 2KB

int ota_flash_get_sector(uint32_t addr)
{
    if (addr < N32_FLASH_BASE || addr >= (N32_FLASH_BASE + N32_FLASH_SIZE)) {
        return -1; // Invalid address
    }
    return (addr - N32_FLASH_BASE) / N32_PAGE_SIZE;  // Each sector is 2KB
}

int ota_flash_check_align(uint32_t addr)
{
    if (addr < N32_FLASH_BASE || addr >= (N32_FLASH_BASE + N32_FLASH_SIZE)) {
        return -1; // Invalid address
    }
    if ((addr % N32_PAGE_SIZE) == 0) {
        return 0;
    }
    return -1;
}

int ota_flash_erase(uint32_t addr, uint32_t length)
{
    int first_sector = ota_flash_get_sector(addr);
    int last_sector = ota_flash_get_sector(addr + length - 1);
    if (first_sector == -1 || last_sector == -1 || last_sector < first_sector) {
        return -1; // Invalid sector
    }
    FLASH_Unlock();
    for (int i = first_sector; i <= last_sector; i++) {
        uint32_t page_address = N32_FLASH_BASE + (i * N32_PAGE_SIZE);
        if (FLASH_EraseOnePage(page_address) != FLASH_COMPL) {
            FLASH_Lock();
            return -2;
        }
    }
    FLASH_Lock();
    return 0;
}

int ota_flash_write(uint32_t addr, const uint8_t* data, uint32_t len)
{
    if (len <= 0 || len > N32_FLASH_SIZE ||
        addr < N32_FLASH_BASE || addr >= (N32_FLASH_BASE + N32_FLASH_SIZE) ||
        (addr + len) > (N32_FLASH_BASE + N32_FLASH_SIZE) ||
        !data)
    {
        return -1;
    }

    /* program */
    uint32_t write_word = (len - 1) / 4 + 1;
    FLASH_Unlock();
    for (uint32_t i = 0; i < write_word; i++ ) {
        if (FLASH_COMPL != FLASH_ProgramWord( addr + i * 4, *(uint32_t*)(data + i * 4))) {
            FLASH_Lock();
            return -2;
        }
    }
    FLASH_Lock();

    /* check */
    for (uint32_t i = 0; i < write_word; i ++) {
        volatile uint32_t *src_ptr = (volatile uint32_t *)(data + i * 4);
        volatile uint32_t *dst_ptr = (volatile uint32_t *)(addr + i * 4);
        if (*src_ptr != *dst_ptr) {
            return -3;
        }
    }

    return 0;
}
